﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
using ASPItalia.ModelVirtualCasting.WPF.ViewModels.Command;
using System.ComponentModel;
using Microsoft.Expression.Encoder.Live;
using Microsoft.Expression.Encoder;
using System.IO;
using ASPItalia.ModelVirtualCasting.WPF.Models;
using System.Net;
using System.Runtime.InteropServices;

namespace ASPItalia.ModelVirtualCasting.WPF.ViewModels
{
    public class RecordViewModel : AsyncViewModel
    {

        private bool _isRecording;
        private bool _isPreparing;
        private bool _isUploading;

        private LiveJob liveJob;
        private string tempFileName;
        private MultimediaMovie _multimedia;

        private string _recordCommandText;

        public RecordViewModel()
        {
            this.RecordCommand = new RelayCommand(Record, CanRecord);

            this.tempFileName = Path.GetTempFileName();

            this.IsRecording = false;
        }

        #region Properties

        /// <summary>
        /// Start to record a new movie
        /// </summary>
        public ICommand RecordCommand { get; private set; }

        public string RecordCommandText
        {
            get { return _recordCommandText; }
            private set
            {
                _recordCommandText = value;
                OnPropertyChanged("RecordCommandText");
            }
        }

        /// <summary>
        /// Get or set the window handle used to show recording preview. Changes to this property affects only to next recording session
        /// </summary>
        public PreviewWindow PreviewWindow { get; set; }

        /// <summary>
        /// Get if application is preparing for recording
        /// </summary>
        public bool IsPreparing
        {
            get
            {
                return _isPreparing;
            }
            private set
            {
                if (_isPreparing != value)
                {
                    _isPreparing = value;
                    OnPropertyChanged("IsPreparing", !value, value);

                    // Invalidate command status
                    CommandManager.InvalidateRequerySuggested();
                }
            }
        }

        /// <summary>
        /// Get if application is uploading the new movie
        /// </summary>
        public bool IsUploading
        {
            get
            {
                return _isUploading;
            }
            private set
            {
                if (_isUploading != value)
                {
                    _isUploading = value;
                    OnPropertyChanged("IsUploading", !value, value);

                    // Invalidate command status
                    CommandManager.InvalidateRequerySuggested();
                }
            }
        }

        /// <summary>
        /// Get if application is recording webcam image
        /// </summary>
        public bool IsRecording
        {
            get
            {
                return _isRecording;
            }
            private set
            {
                if (_isRecording != value)
                {
                    _isRecording = value;
                    OnPropertyChanged("IsRecording", !value, value);

                    // Invalidate command status
                    CommandManager.InvalidateRequerySuggested();
                }

                // Change command text to show
                if (value)
                    this.RecordCommandText = "Stop";
                else
                    this.RecordCommandText = "Start";
            }
        }

        /// <summary>
        /// Get registered movie multimedia
        /// </summary>
        public MultimediaMovie Multimedia
        {
            get
            {
                return _multimedia;
            }
            private set
            {
                _multimedia = value;
                OnPropertyChanged("Multimedia");
            }
        }

        #endregion

        private bool CanRecord()
        {
            return !this.IsPreparing && !this.IsUploading;
        }

        private void Record()
        {
            if (!this.IsRecording && !this.IsPreparing)
                this.ExecuteOperationAsync(DoRecord);
            else if (this.IsRecording)
                this.ExecuteOperationAsync(StopRecord);
        }

        private void StopRecord(AsyncOperation operation)
        {
            try
            {
                if (this.liveJob != null)
                {
                    this.liveJob.StopEncoding();
                    this.liveJob.Dispose();

                    this.Multimedia = new MultimediaMovie(new Uri(this.tempFileName));
                }
                this.liveJob = null;
            }
            finally
            {
                operation.Post(s =>
                {
                    this.IsPreparing = false;
                    this.IsRecording = false;
                    this.IsUploading = true;
                }, null);
            }

            try
            {
                Uri uri = new Uri(Configuration.BaseImageUrl + Guid.NewGuid().ToString("N") + ".wmv");
                if (uri.IsFile)
                {
                    // Fake wait)
                    System.Threading.Thread.Sleep(10000);
                    File.Move(this.tempFileName, System.IO.Path.GetFullPath(uri.AbsolutePath));
                }
                else
                {
                    WebClient client = new WebClient();
                    client.UploadFile(uri, "PUT", this.tempFileName);
                }

                this.Multimedia = new MultimediaMovie(uri);
            }
            finally
            {
                operation.Post(s => this.IsUploading = false, null);
            }
        }

        private void DoRecord(AsyncOperation operation)
        {
            operation.Post(s => this.IsPreparing = true, null);

            this.liveJob = new LiveJob();

            // Add webcam and mic sources
            LiveDeviceSource source = this.liveJob.AddDeviceSource(liveJob.VideoDevices.First(), liveJob.AudioDevices.First());
            this.liveJob.ActivateSource(source);

            // Set output format and bit rates
            this.liveJob.ApplyPreset(LivePresets.VC1512kDSL4x3);

            this.liveJob.OutputFileName = this.tempFileName;

            // Set preview window if present
            if (this.PreviewWindow.IsNotNull())
                this.liveJob.OutputPreviewWindow = this.PreviewWindow;

            this.liveJob.StartEncoding();

            operation.Post(s =>
                {
                    this.IsRecording = true;
                    this.IsPreparing = false;
                }, null);
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);

            if (disposing)
            {
                this.IsRecording = false;
                this.IsPreparing = false;
                this.IsUploading = false;
            }

            if (liveJob != null)
            {
                if (liveJob.IsCapturing)
                    liveJob.StopEncoding();
                liveJob.Dispose();
            }
        }

    }
}
